/*
 * Copyright (c) 2021 KaiHong Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<poll.h>

#include "input/camera_input.h"
#include "input/camera_manager.h"
#include "surface.h"

#include <cinttypes>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <sstream>

#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>

#include <securec.h>

using namespace std;
using namespace OHOS;
using namespace OHOS::CameraStandard;


#define MSG(args...) printf(args) 
 
//函数声明
static int gpio_export(int pin);
static int gpio_unexport(int pin);
static int gpio_direction(int pin, int dir);
static int gpio_write(int pin, int value);
static int gpio_read(int pin);
static int gpio_edge(int pin, int edge);



static int gpio_export(int pin)  
{  
    char buffer[64];  
    int len;  
    int fd;  
    int c_buffer;
  
    fd = open("/sys/class/gpio/export", O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open export for writing!\n");  
        return(-1);  
    }  
    c_buffer = sizeof(buffer);
    len = snprintf(buffer, c_buffer, "%d", pin);  
    //printf("%s,%d,%d\n",buffer,c_buffer,len);
    if (write(fd, buffer, len) < 0) 
    {  
        MSG("Failed to export gpio!");  
        return -1;  
    }  
     
    close(fd);  
    return 0;  
}  
static int gpio_unexport(int pin)  
{  
    char buffer[64];  
    int len;  
    int fd;  
  
    fd = open("/sys/class/gpio/unexport", O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open unexport for writing!\n");  
        return -1;  
    }  
  
    len = snprintf(buffer, sizeof(buffer), "%d", pin);  
    if (write(fd, buffer, len) < 0) 
    {  
        MSG("Failed to unexport gpio!");  
        return -1;  
    }  
     
    close(fd);  
    return 0;  
} 
//dir: 0-->IN, 1-->OUT
static int gpio_direction(int pin, int dir)  
{  
    static const char dir_str[] = "in\0out";  
    char path[64];  
    int fd;  
  
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", pin);  
    fd = open(path, O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open gpio direction for writing!\n");  
        return -1;  
    }  
  
    if (write(fd, &dir_str[dir == 0 ? 0 : 3], dir == 0 ? 2 : 3) < 0) 
    {  
        MSG("Failed to set direction!\n");  
        return -1;  
    }  
  
    close(fd);  
    return 0;  
}  
//value: 0-->LOW, 1-->HIGH
static int gpio_write(int pin, int value)  
{  
    static const char values_str[] = "01";  
    char path[64];  
    int fd;  
  
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);  
    fd = open(path, O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open gpio value for writing!\n");  
        return -1;  
    }  
  
    if (write(fd, &values_str[value == 0 ? 0 : 1], 1) < 0) 
    {  
        MSG("Failed to write value!\n");  
        return -1;  
    }  
  
    close(fd);  
    return 0;  
}
static int gpio_read(int pin)  
{  
    char path[64];  
    char value_str[3];  
    int fd;  
  
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);  
    fd = open(path, O_RDONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open gpio value for reading!\n");  
        return -1;  
    }  
  
    if (read(fd, value_str, 3) < 0)
    {  
        MSG("Failed to read value!\n");  
        return -1;  
    }  
  
    close(fd);  
    return (atoi(value_str));
}  

static int gpio_edge(int pin, int edge)
{
const char dir_str[] = "none\0rising\0falling\0both"; 
int ptr;
char path[64];  
    int fd; 
switch(edge)
{
    case 0:
        ptr = 0;
        break;
    case 1:
        ptr = 5;
        break;
    case 2:
        ptr = 12;
        break;
    case 3:
        ptr = 20;
        break;
    default:
        ptr = 0;
} 
  
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/edge", pin);  
    fd = open(path, O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open gpio edge for writing!\n");  
        return -1;  
    }  
  
    if (write(fd, &dir_str[ptr], strlen(&dir_str[ptr])) < 0) 
    {  
        MSG("Failed to set edge!\n");  
        return -1;  
    }  
  
    close(fd);  
    return 0;  
}

enum class mode_ {
    MODE_PREVIEW = 0,
    MODE_PHOTO
};

// 获取当前时间
static uint64_t GetCurrentLocalTimeStamp()
{
    std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> tp =
        std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
    auto tmp = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
    return tmp.count();
}

// 保存图片
static int32_t SaveYUV(mode_ mode, const char *buffer, int32_t size)
{
    static const std::int32_t FILE_PERMISSION_FLAG = 00766;
    char path[PATH_MAX] = {0};
    int32_t retVal;

    if (mode == mode_::MODE_PREVIEW) {
        system("mkdir -p /data/preview");
        retVal = sprintf_s(path, sizeof(path) / sizeof(path[0]), "/data/preview/%s_%lld.yuv", "preview",
            GetCurrentLocalTimeStamp());
    } else {
        system("mkdir -p /data/capture");
        retVal = sprintf_s(path, sizeof(path) / sizeof(path[0]), "/data/capture/%s_%lld.jpg", "photo",
            GetCurrentLocalTimeStamp());
    }
    if (retVal < 0) {
        printf("Path Assignment failed");
        return -1;
    }
    printf("%s, saving file to %s", __FUNCTION__, path);
    int imgFd = open(path, O_RDWR | O_CREAT, FILE_PERMISSION_FLAG);
    if (imgFd == -1) {
        printf("%s, open file failed, errno = %s.", __FUNCTION__, strerror(errno));
        return -1;
    }
    int ret = write(imgFd, buffer, size);
    if (ret == -1) {
        printf("%s, write file failed, error = %s.", __FUNCTION__, strerror(errno));
        close(imgFd);
        return -1;
    }
    close(imgFd);
    return 0;
}


class CaptureSurfaceListener : public IBufferConsumerListener {
public:
    mode_ mode;
    sptr<Surface> surface_;

    void OnBufferAvailable() override
    {
        int32_t flushFence = 0;
        int64_t timestamp = 0;
        OHOS::Rect damage; // initialize the damage

        OHOS::sptr<OHOS::SurfaceBuffer> buffer = nullptr;
        surface_->AcquireBuffer(buffer, flushFence, timestamp, damage);
        if (buffer != nullptr) {
            char *addr = static_cast<char *>(buffer->GetVirAddr());
            int32_t size = buffer->GetSize();

            // Save the buffer(addr) to a file.
            SaveYUV(mode, addr, size);

            surface_->ReleaseBuffer(buffer, -1);
        }
    }
};


int main()  
{  
    int gpio_fd;
    extern int ret;
    struct pollfd fds[1];
   
    //41为红灯, 1为1号按键
 
    gpio_unexport(41);
    gpio_unexport(1);
 
    
    //41红灯亮起
    gpio_export(41);
    gpio_direction(41, 1);//output out
    gpio_write(41, 1);
    
    //1按钮初始化
    gpio_export(1);
    gpio_direction(1, 0);//input in
    gpio_edge(1,2);
    gpio_fd = open("/sys/class/gpio/gpio1/value",O_RDONLY);
    if(gpio_fd < 0)
    {
        MSG("Failed to open value!\n");  
        return -1;  
    }
    fds[0].fd = gpio_fd;
    fds[0].events  = POLLPRI;

    // 参考Camera组件拍摄 https://gitee.com/openharmony/multimedia_camera_standard
    // 样例:foundation/multimedia/camera_standard/interfaces/innerkits/native/test/camera_capture.cpp

    // 2.获取相机管理器实例并获取相机对象列表。
    int32_t intResult = -1;
    sptr<CameraManager> camManagerObj = CameraManager::GetInstance();
    std::vector<sptr<CameraInfo>> cameraObjList = camManagerObj->GetCameras();
 
    for (auto& it : cameraObjList) {
        printf("Camera ID: %s\n", it->GetID().c_str());
    }

    //3. 创建采集会话。
    sptr<CaptureSession> captureSession = camManagerObj->CreateCaptureSession();
    if (captureSession == nullptr) {
        printf("Failed to create capture session");
        return -1;
    }
    //4. 开始配置采集会话。
    captureSession->BeginConfig();

    //5. 使用相机对象创建相机输入。
    sptr<CaptureOutput> photoOutput;
    sptr<CaptureInput> cameraInput = camManagerObj->CreateCameraInput(cameraObjList[0]);
    if (cameraInput != nullptr) {
        //6.将相机输入添加到采集会话。
        intResult = captureSession->AddInput(cameraInput);
        if (intResult == 0) {
            //7.创建消费者Surface并注册监听器以监听缓冲区更新。拍照的宽和高可以配置为所支持的 1280x960 分辨率。
            sptr<Surface> photoSurface = Surface::CreateSurfaceAsConsumer();
            photoSurface->SetDefaultWidthAndHeight(1280, 960);
            sptr<CaptureSurfaceListener> capturelistener = new CaptureSurfaceListener();
            capturelistener->mode = mode_::MODE_PHOTO;
            capturelistener->surface_ = photoSurface;
            photoSurface->RegisterConsumerListener((sptr<IBufferConsumerListener> &)capturelistener);

            //8.使用上面创建的 Surface 创建拍照输出。
            photoOutput = camManagerObj->CreatePhotoOutput(photoSurface);
            if (photoOutput == nullptr) {
                printf("Failed to create PhotoOutput");
                return -1;
            }
            //9.将拍照输出添加到采集会话。
            intResult = captureSession->AddOutput(photoOutput);
            if (intResult != 0) {
                printf("Failed to Add output to session, intResult: %d", intResult);
                return -1;
            }
            //10. 将配置提交到采集会话。
            intResult = captureSession->CommitConfig();
            if (intResult != 0) {
                printf("Failed to Commit config, intResult: %d", intResult);
                return -1;
            }

        }
    }
    
    while(1)
    {

        ret = poll(fds,1,5000);
        if( ret == -1 )
        MSG("poll\n");
        if( fds[0].revents & POLLPRI)
        {
            ret = lseek(gpio_fd,0,SEEK_SET);

            //切换红灯
            int status = gpio_read(41);
            printf("41 = %d\n",status);
            gpio_write(41, 1 - status);

            //11.拍摄照片。
            intResult = ((sptr<PhotoOutput> &)photoOutput)->Capture();
            if (intResult != 0) {
                printf("Failed to capture, intResult: %d", intResult);
                return -1;
            }else{
                printf("Success to capture, intResult: %d", intResult);
            }
            printf("****************分割线******************\n");
        }
        printf("one loop\n");
     
    }
    //12. 释放采集会话资源。
    captureSession->Release();

    return 0;
}
